<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>统计图表节点</title>
  <style>
    #mountNode {
      background:#001528;
    }
    .graph-tooltip {
      position: absolute;
      top: 0;
      left: 0;
      border: 1px solid #e2e2e2;
      border-radius: 4px;
      font-size: 12px;
      color: #545454;
      background-color: rgba(255, 255, 255, 0.9);
      padding: 10px 8px;
      box-shadow: rgb(174, 174, 174) 0px 0px 10px;
    }
  </style>
</head>
<body>
  <div id="mountNode"></div>
  <script src="../build/g6.js"></script>
  <script>
    /**
     *  该案例演示如何使用G6自定义面积图节点
     *  by 镜曦
     * 
    */
    /**
     * 注册一个类似南丁格尔玫瑰一样的节点
     */
    G6.registerNode('circleBar', {
      draw(cfg, group) {
        const size = cfg.size || [40, 40]; // 如果没有 size 时的默认大小
        const width = size[0];
        const height = size[1];
        /*
          G：
          Fan
          x: 扇形圆心的 x 坐标
          y: 扇形圆心的 y 坐标
          rs: 内圈半径
          re: 外圈半径
          startAngle: 起点弧度
          endAngle: 终点弧度
          clockwise: 为true时顺时针渲染，为false时逆时针渲染
        */
        const baseR = 30;
        let nowAngle = 0;
        const everyIncAngle = 2 * Math.PI * (360 / 5 / 5) / 360;
        cfg.details.forEach(cat =>{
          cat.values.forEach(item =>{
            const re = item+baseR;
            const fan = group.addShape('fan', {
              attrs:{
                x:0,
                y:0,
                rs:baseR,
                re:item+baseR,
                startAngle:nowAngle,
                endAngle: nowAngle += everyIncAngle,
                clockwise:false,
                stroke: 'darkgray',
                fill:cat.color,
              }    
            });
            // 加上交互动画
            fan.on('mouseenter', function(evt) {
              fan.animate({
                  re: re  + 8,
                  repeat: false     
              }, 300);
            });
            fan.on('mouseleave', function(evt) {
              fan.animate({
                  re:re,
                  repeat: false     
              }, 300);
            });
            
            // 设置class
            fan.set("className", 'littleCircle');
            
          });
        });
        group.addShape('circle', {
          // attrs: style
          attrs: {
            x: 0, // 居中
            y: 0,
            r: baseR,
            fill: cfg.centerColor,
            stroke:'darkgray',
          }
        });
        if(cfg.label) {
          group.addShape('text', {
            // attrs: style
            attrs: {
              x: 0, // 居中
              y: 0,
              textAlign: 'center',
              textBaseline: 'middle',
              text: cfg.label,
              fill: 'white',
              fontStyle:'bold',
            }
          });
        }
        return group;
      }
    });


    /**
     * 注册一个 分布在圆周上的折线图
     */
    G6.registerNode('circleLine', {
      draw(cfg, group) {
        const size = cfg.size || [40, 40]; // 如果没有 size 时的默认大小
        const width = size[0];
        const height = size[1];
        
        const baseR = 30;
        let nowAngle = 0;
        
            // Ref line
        let refR = baseR;
        const refInc = 10;
        for(let i = 0; i< 5; i++){
          group.addShape('circle', {
            // attrs: style
            attrs: {
              x: 0, // 居中
              y: 0,
              r: refR += refInc,
              stroke:'rgba(255,255,255,0.4)',
              lineDash:[4, 4],
              
            }
          });
        }
        
        const everyIncAngle = 2 * Math.PI * (360 / 5 / 5) / 360;
        cfg.details.forEach(cat =>{
          // 计算一系列点的位置
          const postions = [];
          cat.values.forEach((item, index) =>{        
            const r = baseR + item;
            const xPos = r * Math.cos(nowAngle);
            const yPos = r * Math.sin(nowAngle);
            nowAngle += everyIncAngle;
            postions.push([xPos, yPos]);
            if(index === 4){
              const r = baseR + item;
              const xPos = r * Math.cos(nowAngle );
              const yPos = r * Math.sin(nowAngle );
              postions.push([xPos, yPos]);
            }
          });
          const pathArrayL = postions.map(item =>(["L", ...item]));
          // 添加连线
          const shape = group.addShape('path', {
            attrs: {
              path: [
                ['M', 0, 0 ], // 上部顶点
                ...pathArrayL,
                ['Z'] // 封闭
              ],
              stroke: cat.color // 颜色应用到边上，如果应用到填充，则使用 fill: cfg.color
            }
          });
          // 添加标注点
          postions.forEach(( pos, index )=>{
            if(index !== 5){
                const littleCircle = group.addShape('circle', {
                // attrs: style
                attrs: {
                  x: pos[0], // 居中
                  y: pos[1],
                  r: 2,
                  fill: 'black',
                  stroke:cat.color,
                  cursor: "pointer",
                }
              });
              // 加上交互动画
              littleCircle.on('mouseenter', function(evt) {
                littleCircle.animate({
                    r: 5,
                    repeat: false     
                }, 200);
              });
              littleCircle.on('mouseleave', function(evt) {
                littleCircle.animate({
                    r: 2,
                    repeat: false     
                }, 200);
              });
              // 设置class
              littleCircle.set("className", 'littleCircle');
            }
          
          })
          
          /*
          const shape = group.addShape('path', {
            attrs: {
              path: [
                ['M', 0, 0 ], // 上部顶点
                ['L', width / 2, 0], // 右侧点
                ['L', 0, height / 2], // 下部
                ['L', - width / 2, 0], // 左侧
                ['Z'] // 封闭
              ],
              stroke: cfg.color // 颜色应用到边上，如果应用到填充，则使用 fill: cfg.color
            }
          });
          
          */

        });

          // 添加一个和背景色相同的圆形
        group.addShape('circle', {
          // attrs: style
          attrs: {
            x: 0, // 居中
            y: 0,
            r: baseR,
            fill: cfg.centerColor,
            stroke:'darkgray',
          }
        });
        if(cfg.label) {
          group.addShape('text', {
            // attrs: style
            attrs: {
              x: 0, // 居中
              y: 0,
              textAlign: 'center',
              textBaseline: 'middle',
              text: cfg.label,
              fill: 'white',
              fontStyle:'bold',
            }
          });
        }
        return group;
      }
    });


    /**
     * 注册一个 只有标注点
     */
    G6.registerNode('justPoints', {
      draw(cfg, group) {
        const size = cfg.size || [40, 40]; // 如果没有 size 时的默认大小
        const width = size[0];
        const height = size[1];
        
        const baseR = 30;
        let nowAngle = 0;
        
            // Ref line
        let refR = baseR;
        const refInc = 10;
        for(let i = 0; i< 5; i++){
          group.addShape('circle', {
            // attrs: style
            attrs: {
              x: 0, // 居中
              y: 0,
              r: refR += refInc,
              stroke:'rgba(255,255,255,0.4)',
              lineDash:[4, 4],
              
            }
          });
        }
        const everyIncAngle = 2 * Math.PI * (360 / 5 / 5) / 360;
        nowAngle = nowAngle + everyIncAngle / 2;
        cfg.details.forEach(cat =>{
          // 计算一系列点的位置
          const postions = [];
          cat.values.forEach((item, index) =>{        
            const r = baseR + item;
            const xPos = r * Math.cos(nowAngle);
            const yPos = r * Math.sin(nowAngle);
            nowAngle += everyIncAngle;
            postions.push([xPos, yPos]);
            if(index === 4){
              const r = baseR + item;
              const xPos = r * Math.cos(nowAngle );
              const yPos = r * Math.sin(nowAngle );
              postions.push([xPos, yPos]);
            }
          });


          // 添加标注点
          postions.forEach(( pos, index )=>{
            if(index !== 5){
                group.addShape('circle', {
                // attrs: style
                attrs: {
                  x: pos[0], // 居中
                  y: pos[1],
                  r: 2,
                  fill: 'black',
                  stroke:cat.color,
                }
              });
            }
          
          })
          
          /*
          const shape = group.addShape('path', {
            attrs: {
              path: [
                ['M', 0, 0 ], // 上部顶点
                ['L', width / 2, 0], // 右侧点
                ['L', 0, height / 2], // 下部
                ['L', - width / 2, 0], // 左侧
                ['Z'] // 封闭
              ],
              stroke: cfg.color // 颜色应用到边上，如果应用到填充，则使用 fill: cfg.color
            }
          });
          
          */

        });
        
        let nowAngle2 = 0;
        const everyIncAngleCat = 2 * Math.PI * (360 / 5 ) / 360;
        for(let i = 0; i < 5; i++){
          const r = 30 + 50;
          const xPos = r * Math.cos(nowAngle2);
          const yPos = r * Math.sin(nowAngle2);
          
          const shape = group.addShape('path', {
            attrs: {
              path: [
                ['M', 0, 0 ], 
                ['L', xPos, yPos], 

              ],
              lineDash:[4, 4],
              
              stroke: 'darkgray' // 颜色应用到边上，如果应用到填充，则使用 fill: cfg.color
            }
          });
          nowAngle2 += everyIncAngleCat;
        }
        // 添加一个和背景色相同的圆形
        group.addShape('circle', {
          // attrs: style
          attrs: {
            x: 0, // 居中
            y: 0,
            r: baseR,
            fill: cfg.centerColor,
            stroke:'darkgray',
          }
        });
        
        if(cfg.label) {
          group.addShape('text', {
            // attrs: style
            attrs: {
              x: 0, // 居中
              y: 0,
              textAlign: 'center',
              textBaseline: 'middle',
              text: cfg.label,
              fill: 'white',
              fontStyle:'bold',
            }
          });
        }
        return group;
      }
    });

    /**
     * 注册一个 面积图节点
     */
    G6.registerNode('area', {
      draw(cfg, group) {
        const size = cfg.size || [40, 40]; // 如果没有 size 时的默认大小
        const width = size[0];
        const height = size[1];
        
        const baseR = 30;
        let nowAngle = 0;
        
        // Ref line
        let refR = baseR;
        const refInc = 10;
        for(let i = 0; i< 6; i++){
          group.addShape('circle', {
            // attrs: style
            attrs: {
              x: 0, // 居中
              y: 0,
              r: refR += refInc,
              stroke:'rgba(255,255,255,0.4)',
              lineDash:[4, 4],
              
            }
          });
        }
        const everyIncAngle = 2 * Math.PI * (360 / 5 ) / 360;
        const tempIncValues = [baseR, baseR, baseR, baseR, baseR];
        const allRs = [];
        cfg.details.forEach(cat =>{
          
          const oneRs = [];
          cat.values.forEach((v, i) =>{
            const R = tempIncValues[i] + v * 0.4;
            oneRs.push(R);
            tempIncValues[i] = R;
          });
          allRs.push(oneRs);
          
        });
        const strokeColors = [
          'rgba(37,203,253,1)',
          'rgba(254,255,123,1)',
          'rgba(254,171,58,1)',
          'rgba(254,87,102,1)',
          'rgba(22,193,118,1)',
        ];
        const fillColors = [
          'rgba(37,203,253,0.5)',
          'rgba(254,255,123,0.5)',
          'rgba(254,171,58,0.5)',
          'rgba(254,87,102,0.5)',
          'rgba(22,193,118,0.5)',
        ];
        
        
        allRs.reverse().forEach((Rs, index) =>{
          let curAngle = 0;
          const poss = [];
          Rs.forEach(r=>{
            const xPos = r * Math.cos(curAngle);
            const yPos = r * Math.sin(curAngle);
            curAngle += everyIncAngle;
            poss.push([xPos, yPos]);
          });
          const Ls = poss.map((p, i)=>{
            if( i === 0 ){
              return ["M", ...p]
            }
            return ["L", ...p]
          });
          console.log('Ls', ...Ls);
          const shape = group.addShape('path', {
            attrs: {
              path: [
                ...Ls,
                ['Z'] // 封闭
              ],
              stroke:strokeColors[index] ,
              fill:fillColors[index],
            }
          });
          
        });
        let nowAngle2 = 0;
        const everyIncAngleCat = 2 * Math.PI * (360 / 5 ) / 360;
        for(let i = 0; i < 5; i++){
          const r = 30 + 60;
          const xPos = r * Math.cos(nowAngle2);
          const yPos = r * Math.sin(nowAngle2);
          
          const shape = group.addShape('path', {
            attrs: {
              path: [
                ['M', 0, 0 ], 
                ['L', xPos, yPos], 

              ],
              lineDash:[4, 4],
              
              stroke: 'darkgray' // 颜色应用到边上，如果应用到填充，则使用 fill: cfg.color
            }
          });
          nowAngle2 += everyIncAngleCat;
        }

        // 添加一个和背景色相同的圆形
        group.addShape('circle', {
          // attrs: style
          attrs: {
            x: 0, // 居中
            y: 0,
            r: baseR,
            fill: cfg.centerColor,
            stroke:'darkgray',
          }
        });
        
        if(cfg.label) {
          group.addShape('text', {
            // attrs: style
            attrs: {
              x: 0, // 居中
              y: 0,
              textAlign: 'center',
              textBaseline: 'middle',
              text: cfg.label,
              fill: 'white',
              fontStyle:'bold'
            }
          });
        }
        return group;
      }
    });




    /**
      环 1
    */
    G6.registerNode('rings1', {
      draw(cfg, group) {
        const size = cfg.size || [40, 40]; // 如果没有 size 时的默认大小
        const width = size[0];
        const height = size[1];
        /*
          G：
          Fan
          x: 扇形圆心的 x 坐标
          y: 扇形圆心的 y 坐标
          rs: 内圈半径
          re: 外圈半径
          startAngle: 起点弧度
          endAngle: 终点弧度
          clockwise: 为true时顺时针渲染，为false时逆时针渲染
        */
        const baseR = 30;
        let nowAngle = 0;
        const everyIncAngle = 2 * Math.PI * (360 / 5 / 5) / 360;
        cfg.details.forEach(cat =>{
          cat.values.forEach(item =>{
            const baseNbr = Math.ceil(item / 10);
            const baseIncR = 7;
            let nowStartR = baseR;
            const last = item % 10;
            const endAngle = nowAngle + everyIncAngle;
            for (let i = 0; i < baseNbr ; i ++ ) {
              const fan = group.addShape('fan', {
                attrs:{
                  x:0,
                  y:0,
                  rs:nowStartR,
                  re:nowStartR + baseIncR,
                  startAngle:nowAngle,
                  endAngle:endAngle,
                  clockwise:false,
                  stroke: 'darkgray',
                  fill:cat.color,
                }    
              });
              nowStartR = nowStartR + baseIncR + 2
              if(i === baseNbr -1 && last !== 0){
                const fan = group.addShape('fan', {
                  attrs:{
                    x:0,
                    y:0,
                    rs:nowStartR,
                    re:nowStartR + baseIncR * last / 10,
                    startAngle:nowAngle,
                    endAngle:endAngle,
                    clockwise:false,
                    stroke: 'darkgray',
                    fill:cat.color,
                  }    
                });
              }
            }
            nowAngle = endAngle 
          });
        });
        
        group.addShape('circle', {
          // attrs: style
          attrs: {
            x: 0, // 居中
            y: 0,
            r: baseR,
            fill: cfg.centerColor,
            stroke:'darkgray',
          }
        });
        if(cfg.label) {
          group.addShape('text', {
            // attrs: style
            attrs: {
              x: 0, // 居中
              y: 0,
              textAlign: 'center',
              textBaseline: 'middle',
              text: cfg.label,
              fill: 'white',
              fontStyle:'bold',
            }
          });
        }
        return group;
      }
    });



    /**
     * 注册一个 面积图节点
     */
    G6.registerNode('rings2', {
      draw(cfg, group) {
        const size = cfg.size || [40, 40]; // 如果没有 size 时的默认大小
        const width = size[0];
        const height = size[1];
        
        const baseR = 30;
        let nowAngle = 0;
        
        // Ref line
        let refR = baseR;
        const refInc = 10;
        for(let i = 0; i< 6; i++){
          group.addShape('circle', {
            // attrs: style
            attrs: {
              x: 0, // 居中
              y: 0,
              r: refR += refInc,
              stroke:'rgba(255,255,255,0.4)',
              lineDash:[4, 4],
              
            }
          });
        }
        const everyIncAngle = 2 * Math.PI * (360 / 5 ) / 360;
        const tempIncValues = [baseR, baseR, baseR, baseR, baseR];
        const allRs = [];
        cfg.details.forEach(cat =>{
          
          const oneRs = [];
          cat.values.forEach((v, i) =>{
            const R = tempIncValues[i] + v * 0.4;
            oneRs.push(R);
            tempIncValues[i] = R;
          });
          allRs.push(oneRs);
          
        });
        const strokeColors = [
          'rgba(37,203,253,1)',
          'rgba(254,255,123,1)',
          'rgba(254,171,58,1)',
          'rgba(254,87,102,1)',
          'rgba(22,193,118,1)',
        ];
        const fillColors = [
          'rgba(37,203,253,0.5)',
          'rgba(254,255,123,0.5)',
          'rgba(254,171,58,0.5)',
          'rgba(254,87,102,0.5)',
          'rgba(22,193,118,0.5)',
        ];
        

        
        
        
        allRs.reverse().forEach((Rs, index) =>{
          let curAngle = 0;
          const poss = [];
          Rs.forEach(r=>{
            
            const baseNbr = Math.ceil(r / 10);
            const baseIncR = 7;
            let nowStartR = baseR;
            const last = r % 10;
            
            for(let i = 0; i < baseNbr; i++){
              
              const endAngle = nowAngle + everyIncAngle;
              
              const fan = group.addShape('fan', {
                attrs:{
                  x:0,
                  y:0,
                  rs:nowStartR,
                  re:nowStartR + baseIncR,
                  startAngle:nowAngle,
                  endAngle:endAngle,
                  clockwise:false,
                  stroke: 'darkgray',
                  fill:strokeColors[index],
                }    
              });
              nowStartR = nowStartR + baseIncR + 2
              if(i === baseNbr -1 && last !== 0){
                const fan = group.addShape('fan', {
                  attrs:{
                    x:0,
                    y:0,
                    rs:nowStartR,
                    re:nowStartR + baseIncR * last / 10,
                    startAngle:nowAngle,
                    endAngle:endAngle,
                    clockwise:false,
                    stroke: 'darkgray',
                    fill:strokeColors[index],
                  }    
                });
              }
              nowAngle = endAngle
            }
          });
        });

        // 添加一个和背景色相同的圆形
        group.addShape('circle', {
          // attrs: style
          attrs: {
            x: 0, // 居中
            y: 0,
            r: baseR,
            fill: cfg.centerColor,
            stroke:'darkgray',
          }
        });
        
        if(cfg.label) {
          group.addShape('text', {
            // attrs: style
            attrs: {
              x: 0, // 居中
              y: 0,
              textAlign: 'center',
              textBaseline: 'middle',
              text: cfg.label,
              fill: 'white',
              fontStyle:'bold'
            }
          });
        }
        return group;
      }
    });

    /** 数据 */
    const data = {
      nodes: [{
        id: 'nodeA',
        x: 150,
        y: 150,
        label: 'Bar',
        shape:'circleBar',
        anchorPoints: [
          [0, 0.5], [1, 0.5]
        ],
        details:[
          {cat:'pv', values:[20,30,40,30,30], color:"#25cbfd"},
          {cat:'dal', values:[40,30,20,30,50], color:"#feff7b"},
          {cat:'uv', values:[40,30,30,40,40], color:"#feab3a"},
          {cat:'sal', values:[20,30,50,20,20], color:"#fe5766"},
          {cat:'cal', values:[10,10,20,20,20], color:"#16c176"},
        ],
        centerColor:'#0066FF',
      },
      {
        id: 'nodeB',
        x: 400,
        y: 150,
        label: 'Line',
        shape:'circleLine',
        anchorPoints: [
          [0, 0.5], [1, 0.5]
        ],
        details:[
          {cat:'pv', values:[20,30,40,30,30], color:"#25cbfd"},
          {cat:'dal', values:[40,30,20,30,50], color:"#feff7b"},
          {cat:'uv', values:[40,30,30,40,40], color:"#feab3a"},
          {cat:'sal', values:[20,30,50,20,20], color:"#fe5766"},
          {cat:'cal', values:[10,10,20,20,20], color:"#16c176"},
        ],
        centerColor:'#0066FF',
        
      },
              
      {
        id: 'nodeC',
        x: 650,
        y: 150,
        label: 'Point',
        shape:'justPoints',
        anchorPoints: [
          [0, 0.5], [1, 0.5]
        ],
        details:[
          {cat:'pv', values:[20,30,40,30,30], color:"#25cbfd"},
          {cat:'dal', values:[40,30,20,30,50], color:"#feff7b"},
          {cat:'uv', values:[40,30,30,40,40], color:"#feab3a"},
          {cat:'sal', values:[20,30,50,20,20], color:"#fe5766"},
          {cat:'cal', values:[10,10,20,20,20], color:"#16c176"},
        ],
        centerColor:'#0066FF',
        
      },
              
      {
        id: 'nodeD',
        x: 150,
        y: 400,
        label: 'Area',
        shape:'area',
        anchorPoints: [
          [0, 0.5], [1, 0.5]
        ],
        details:[
          {cat:'pv', values:[20,30,40,30,30], color:"#25cbfd"},
          {cat:'dal', values:[40,30,20,30,50], color:"#feff7b"},
          {cat:'uv', values:[40,30,30,40,40], color:"#feab3a"},
          {cat:'sal', values:[20,30,50,20,20], color:"#fe5766"},
          {cat:'cal', values:[10,10,20,20,20], color:"#16c176"},
        ],
        centerColor:'#0066FF',
        
      },
              
      {
        id: 'nodeF',
        x: 400,
        y: 400,
        label: 'Rings1',
        shape:'rings1',
        anchorPoints: [
          [0, 0.5], [1, 0.5]
        ],
        details:[
          {cat:'pv', values:[20,30,48,30,30], color:"#25cbfd"},
          {cat:'dal', values:[40,30,20,30,50], color:"#feff7b"},
          {cat:'uv', values:[40,30,30,4,40], color:"#feab3a"},
          {cat:'sal', values:[20,30,50,20,20], color:"#fe5766"},
          {cat:'cal', values:[10,10,25,20,20], color:"#16c176"},
        ],
        centerColor:'#0066FF',
        
      },
    

    ],
      edges: [
        //{source:'nodeA', target:'nodeB', color:"rgba(0, 255, 255, 0.5)"},
        //{source:'nodeB', target:'nodeC',  color:"rgba(0, 255, 255, 0.5)"},
        
      ]
    };

    const graph = new G6.Graph({
      container: 'mountNode',
      width: 1000,
      height: 600
    });

    graph.on("node:mouseenter", function(event) {
      var node = event.item;
      var nodeId = node.get("model").id;
      var shape = event.target;

      if (shape.get("className") === "littleCircle") {
        // 如果点击是发生在节点里面的小圆上，显示tooltip
        console.log('x', event);
        console.log('Y', event);
        
        showTooltip("tooltip for " + nodeId, {
          x: event.x,
          y: event.y
        });
      } else {
        // 否则隐藏tooltip
        hideTooltip();
      }
    });
    graph.on("node:mouseleave", function(event) {
        hideTooltip();
    });


    graph.data(data);
    graph.render();

    var tooltipEl = null;
    // 在指定的位置显示tooltip
    function showTooltip(message, position) {
      const offSetX = 50;
      if (!tooltipEl) {
        var container = document.getElementById("mountNode");
        tooltipEl = document.createElement("div");
        tooltipEl.setAttribute("class", "graph-tooltip");
        container.appendChild(tooltipEl);
      }
      tooltipEl.textContent = message;
      // tooltip是相对于画布canvas element绝对定位，所以position的x，y必须是相对于画布的坐标
      tooltipEl.style.left = position.x + offSetX+ "px";
      tooltipEl.style.top = position.y + "px";
      tooltipEl.style.display = "block";
    }

    // 隐藏tooltip
    function hideTooltip() {
      if (!tooltipEl) {
        return;
      }
      tooltipEl.style.display = "none";
    }
  </script>
</body>
</html>